// Copyright (c) 2008-2010, Arne Claus
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
// following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 
//   disclaimer.
// - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
//   disclaimer in the documentation and/or other materials provided with the distribution.
// - Neither the name Arne Claus nor the names of any contributors may be used to endorse or promote products
//   derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef __INCLUDED_CBL_MATRIX_H__
#define __INCLUDED_CBL_MATRIX_H__

#include "CBL_Defines.h"
#include "CBL_Debug.h"
#include "CBL_Math.h"
#include "CBL_Meta.h"

#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <iostream>

// *************************************************************************************************
//! \class	CMatrix
//! \brief	Generic class for matrices.
//!			Note that you can switch between the (default) row- and column-major setup by setting 
//!			the macro CBL_MATRIX_COL_MAJOR.
// *************************************************************************************************

CB_SUBNAMESPACE_START(Math)

template <typename TType, uint32 TMajorSize, uint32 TMinorSize>
class CMatrix
{
public:
	
	//! \note	Matrix will be initialized as identity.
	CMatrix();
    
    //! \note	No members will be initialized.
    CMatrix(EInitType) {}
	
	//! \note	This function expects to be called with an array of exactly TMajorSize * TMinorSize
	//!			entries. If this is not the case, there are two possibilities:
	//!			a) More arguments are provided -> spare entries will be ignored
	//!			b) Less arguments are provided -> undefined behavior for the remaining entries
	//!			Make sure that the data is alignend according to the set row-/column major setting.
	//!			Default (row major) would be r1c1, r1c2, r1c3, ..., rnc1, rnc2, ... rncn
	CMatrix(const TType* _pData);
	
	
	//! \note	This function will only use the intersecting members.
	//!			All others will be initialized with 0.
	template <typename TType2, uint32 TMajorSize2, uint32 TMinorSize2>
	CMatrix(const CMatrix<TType2, TMajorSize2, TMinorSize2>& _rOther);
	
public: 
	
	//! \note	This function will only override intersecting members.
	//!			It will fail if a static_cast conversion is not possible.
	template <typename TType2, uint32 TMajorSize2, uint32 TMinorSize2>
	CB_FORCEINLINE void CB_INLINE_ATTR operator = (const CMatrix<TType2, TMajorSize2, TMinorSize2>& _rOther);
	
	//! \note	This function expects to be called with an array of exactly TSize entries.
	//!			If this is not the case, there are two possibilities:
	//!			a) More arguments are provided -> spare entries will be ignored
	//!			b) Less arguments are provided -> undefined behavior for the remaining entries
	CB_FORCEINLINE void CB_INLINE_ATTR operator = (const TType* _pData);
	
public:
	
	template <typename TType2, uint32 TMinorSize2>
	CB_FORCEINLINE CMatrix& CB_INLINE_ATTR operator *= (const CMatrix<TType2, TMinorSize, TMinorSize2>& _rOther);
	
	CB_FORCEINLINE CMatrix& CB_INLINE_ATTR operator *= (const TType& _rOther);
	
	CB_FORCEINLINE bool CB_INLINE_ATTR AlmostEq (const CMatrix& _rOther, float _Epsilon=0.0001f);

public: 
	
	CB_FORCEINLINE TType* CB_INLINE_ATTR operator [] (uint32 _Index);
	CB_FORCEINLINE const TType* CB_INLINE_ATTR operator [] (uint32 _Index) const;
	
	CB_FORCEINLINE TType* CB_INLINE_ATTR Array();
	CB_FORCEINLINE const TType* CB_INLINE_ATTR Array() const;
	
public: 
	
	static const uint32		s_MemSize = sizeof(TType) * TMajorSize * TMinorSize;
	
protected: 
	
	TType					m_Values[TMajorSize][TMinorSize];
};

// -------------------------------------------------------------------------------------------------
//	Operators
// -------------------------------------------------------------------------------------------------

template <typename TLhsType, typename TRhsType, uint32 TLhsMajorSize, uint32 TSharedSize, uint32 TRhsMinorSize>
CB_FORCEINLINE CMatrix<TLhsType, TLhsMajorSize, TSharedSize> CB_INLINE_ATTR operator * (CMatrix<TLhsType, TLhsMajorSize, TSharedSize> _Lhs, const CMatrix<TRhsType, TSharedSize, TRhsMinorSize>& _rRhs);

template <typename TType, uint32 TMajorSize, uint32 TMinorSize>
CB_FORCEINLINE std::ostream& CB_INLINE_ATTR operator << (std::ostream& _rLhs, CMatrix<TType, TMajorSize, TMinorSize>& _rRhs);

// -------------------------------------------------------------------------------------------------
//	Tools
// -------------------------------------------------------------------------------------------------

template <typename TType, uint32 TMajorSize, uint32 TMinorSize>
CB_FORCEINLINE void CB_INLINE_ATTR SetIdentity(CMatrix<TType, TMajorSize, TMinorSize>& _rMatrix);

template <typename TType, uint32 TMajorSize, uint32 TMinorSize>
CB_FORCEINLINE CMatrix<TType, TMajorSize, TMinorSize> CB_INLINE_ATTR Transpose(const CMatrix<TType, TMajorSize, TMinorSize>& _rMtx);

template <typename TType, uint32 TSize>
CB_FORCEINLINE CMatrix<TType, TSize-1, TSize-1> CB_INLINE_ATTR Split(const CMatrix<TType, TSize, TSize>& _rMtx, uint32 _AtMajor, uint32 _AtMinor);

template <typename TType, uint32 TSize>
CB_FORCEINLINE TType CB_INLINE_ATTR Determinant(const CMatrix<TType, TSize, TSize>& _rMtx);

template <typename TType, uint32 TSize>
CB_FORCEINLINE bool CB_INLINE_ATTR IsInverible(const CMatrix<TType, TSize, TSize>& _rMtx);

template <typename TType, uint32 TSize>
CB_FORCEINLINE CMatrix<TType, TSize, TSize> CB_INLINE_ATTR Invert(const CMatrix<TType, TSize, TSize>& _rMtx);


// -------------------------------------------------------------------------------------------------
//  Types for general usage
// -------------------------------------------------------------------------------------------------

#include "CBL_MatrixTypes.hpp"

typedef CMatrix44<float>	Float4x4;
typedef CMatrix33<float>	Float3x3;
typedef CMatrix22<float>	Float2x2;

// -------------------------------------------------------------------------------------------------
//  Type dependent tools
// -------------------------------------------------------------------------------------------------

CB_FORCEINLINE Float4x4 CB_INLINE_ATTR Translation3D(float _X, float _Y, float _Z);
CB_FORCEINLINE Float4x4 CB_INLINE_ATTR Scale3D(float _X, float _Y, float _Z);
CB_FORCEINLINE Float4x4 CB_INLINE_ATTR Rotation3D(float _XDeg, float _YDeg, float _ZDeg);

CB_FORCEINLINE Float3x3 CB_INLINE_ATTR Translation2D(float _X, float _Y);
CB_FORCEINLINE Float3x3 CB_INLINE_ATTR Scale2D(float _X, float _Y);
CB_FORCEINLINE Float3x3 CB_INLINE_ATTR Rotation2D(float _Deg);

// -------------------------------------------------------------------------------------------------
//  Implementation
// -------------------------------------------------------------------------------------------------

#include "CBL_Matrix.hpp"

CB_SUBNAMESPACE_END

#endif